/**
 * Copyright 2013 yangming.liu<liuyangming@gmail.com>.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, see <http://www.gnu.org/licenses/>.
 */
package org.bytesoft.openjta.recovery;

import java.util.List;

import javax.transaction.HeuristicCommitException;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;

import org.bytesoft.openjta.TransactionImpl;
import org.bytesoft.openjta.archive.XAResourceArchive;
import org.bytesoft.openjta.coordinate.TransactionCoordinator;
import org.bytesoft.openjta.internal.RollbackRequiredException;
import org.bytesoft.openjta.internal.TransactionVote;

public abstract class RecoveredTransaction extends TransactionImpl {
	private static final long serialVersionUID = 1L;

	protected boolean recoveryReady;
	protected boolean recoveryRollbackRequired;

	@Override
	public synchronized int xaPrepare() throws SystemException, RollbackRequiredException {
		if (this.transactionStatus.isCommitting() || this.transactionStatus.isCommitFail()
				|| this.transactionStatus.isCommitted()) {
			return TransactionVote.XA_COMMIT_REQUIRED;
		} else if (this.transactionStatus.isPrepared()) {
			return TransactionVote.XA_OK;
		}
		throw new RollbackRequiredException();
	}

	@Override
	public synchronized void xaCommit() throws SecurityException, HeuristicMixedException, HeuristicRollbackException,
			RollbackException, SystemException {
		if (this.transactionStatus.isCommitted()) {
			return;
		} else if (this.transactionStatus.isActive()//
				|| this.transactionStatus.isRollbackOnly()//
				|| this.transactionStatus.isPreparing()//
				|| this.transactionStatus.isPrepareFail()//
				|| this.transactionStatus.isRollingBack()//
				|| this.transactionStatus.isRollbackFail()//
				|| this.transactionStatus.isRolledBack()) {
			try {
				this.xaRollback();
				throw new HeuristicRollbackException();
			} catch (HeuristicCommitException ex) {
				return;
			}
		}

		TransactionCoordinator coordinator = this.selectTransactionCoordinator();

		coordinator.recoveryXACommit(this.coordinateContext);
	}

	@Override
	public synchronized void xaRollback() throws SecurityException, HeuristicMixedException, HeuristicCommitException,
			SystemException {
		if (this.transactionStatus.isRolledBack()) {
			return;
		} else if (this.transactionStatus.isCommitted()) {
			throw new HeuristicCommitException();
		}

		TransactionCoordinator coordinator = this.selectTransactionCoordinator();

		coordinator.recoveryXARollback(this.coordinateContext);
	}

	@Override
	public synchronized void processCommit() throws HeuristicMixedException, HeuristicRollbackException,
			RollbackException, SecurityException, SystemException {

		if (this.transactionStatus.isCommitted()) {
			return;
		} else if (this.transactionStatus.isActive()//
				|| this.transactionStatus.isRollbackOnly()//
				|| this.transactionStatus.isPreparing()//
				|| this.transactionStatus.isPrepareFail()//
				|| this.transactionStatus.isRollingBack()//
				|| this.transactionStatus.isRollbackFail()//
				|| this.transactionStatus.isRolledBack()) {
			this.processRollback();
			throw new HeuristicRollbackException();
		}

		TransactionCoordinator coordinator = this.selectTransactionCoordinator();
		coordinator.processRecoveryCommit(this.coordinateContext);
	}

	@Override
	public synchronized void processRollback() throws IllegalStateException, SystemException {

		if (this.transactionStatus.isRolledBack()) {
			return;
		} else if (this.transactionStatus.isCommitted()) {
			throw new IllegalStateException();
		}

		try {
			TransactionCoordinator coordinator = this.selectTransactionCoordinator();
			coordinator.processRecoveryRollback(this.coordinateContext);
		} catch (HeuristicMixedException ex) {
			SystemException exp = new SystemException();
			exp.initCause(exp);
			throw exp;
		} catch (HeuristicCommitException ex) {
			SystemException exp = new SystemException();
			exp.initCause(exp);
			throw exp;
		}
	}

	public void regulateTransaction() throws IllegalStateException {
		if (this.transactionStatus.isActive()) {
			// never happen
		} else if (this.transactionStatus.isRollbackOnly()) {
			// never happen
		} else if (this.transactionStatus.isStatusPreparing(true)) {
			boolean failureExists = this.transactionStatus.isPrepareFail();
			this.regulatePreparingTransaction(failureExists);
		} else if (this.transactionStatus.isPrepared()) {
			this.regulatePreparedTransaction();
		} else if (this.transactionStatus.isStatusCommitting(true)) {
			boolean failureExists = this.transactionStatus.isCommitFail();
			this.regulateCommittingTransaction(failureExists);
		} else if (this.transactionStatus.isStatusRollingBack(true)) {
			boolean failureExists = this.transactionStatus.isRollbackFail();
			this.regulateRollingbackTransaction(failureExists);
		} else if (this.transactionStatus.isCommitted()) {
			// ignore
		} else if (this.transactionStatus.isRolledBack()) {
			// ignore
		} else {
			// never happen
		}
	}

	@Override
	public List<XAResourceArchive> getXAResourceArchives() {
		return this.xaResourceArchives;
	}

	protected abstract void regulatePreparingTransaction(boolean failureExists) throws IllegalStateException;

	protected abstract void regulatePreparedTransaction() throws IllegalStateException;

	protected abstract void regulateCommittingTransaction(boolean failureExists) throws IllegalStateException;

	protected abstract void regulateRollingbackTransaction(boolean failureExists) throws IllegalStateException;

	public boolean isRecoveryRollbackRequired() {
		return recoveryRollbackRequired;
	}

	public void setRecoveryRollbackRequired(boolean recoveryRollbackRequired) {
		this.recoveryRollbackRequired = recoveryRollbackRequired;
	}

	public boolean isRecoveryReady() {
		return recoveryReady;
	}

	public void setRecoveryReady(boolean recoveryReady) {
		this.recoveryReady = recoveryReady;
	}

}
